/**********************************************************************************
// Asteroids Race 
//
// Creation:	17 Out 2009
// Updated:		18 Apr 2011
// Compiler:	Visual Studio 2010
//
// Notes:		Level 2
//
**********************************************************************************/

#include "Asteroids.h"

/**********************************************************************************/


void Asteroids::Level2Init()
{
	// ----------------------------
	// PreLevel 2 Initialization
	// ----------------------------

	// create tutorial like ballons

	// ballon zero: reach the finish marker	
	Point ballonZero[7] = 
	{ 
	  Point(-3,0),		// 1
	  Point(4,-8),		// 2
	  Point(55,-8),		// 3
	  Point(55,-32),	// 4
	  Point(-15,-32),	// 5
	  Point(-15,-8),	// 6
	  Point(-4,-8)		// 7
	};
	ballons[0].SetVertices(ballonZero, 7);
	ballons[0].MoveTo(midX+180, midY+220);
	ballons[0].color = Color(192,128,0);
	ballons[0].Scale(3.0f, 2.5f);

	// ballon one: destroy all the waste	
	Point ballonOne[7] = 
	{ 
	  Point(-3,0),    // 1
	  Point(-4,-8),   // 2
	  Point(-55,-8),  // 3
	  Point(-55,-30), // 4
	  Point(15,-30),  // 5
	  Point(15,-8),   // 6
	  Point(4,-8)     // 7
	};
	ballons[1].SetVertices(ballonOne, 7);
	ballons[1].MoveTo(midX-265, midY-165);
	ballons[1].color = Color(192,128,0);
	ballons[1].Scale(2.6f, 2.5f);

	// ballon two: move to START	
	Point ballonTwo[4] = 
	{ 
	  Point(-37,-11), // 1
	  Point(37,-11),  // 2
	  Point(37,11),   // 3
	  Point(-37,11)   // 4
	};
	ballons[2].SetVertices(ballonTwo, 4);
	ballons[2].MoveTo(midX, midY+25);
	ballons[2].color = Color(0,255,0);
	ballons[2].Scale(1.8f, 2.5f);

	// ----------------------------
	// Level 2 Initialization
	// ----------------------------

	// reset ship to default location, speed and direction
	Point shipVertex[4] = {Point(0,-10), Point(7,10), Point(0,7), Point(-7,10)};
	ship.Reset();
	ship.SetVertices(shipVertex, 4);
	ship.MoveTo(midX-150, midY+280);
	ship.Rotate(-45);
	ship.color = Color(255,255,0);
	scene.RegisterMoving(ship);

	// create finish marker
	finPosX = midX+150;
	finPosY = midY+280;
	finish = Rect(-40, -10, 40, 10);
	finish.MoveTo(finPosX,finPosY);
	finish.color = Color(0,255,0);
	finish.customType = objFinish;
	scene.RegisterStatic(finish);
	
	// vertical bar
	Rect * bar = new Rect(midX-13.0f, midY+200.0f, midX+13.0f, GetWindowHeight()-40.0f);
	bar->color = Color(128,128,128);
	bar->customType = objFixed;
	fixed.push_back(bar);
	scene.RegisterStatic(*bar);

	bar = new Rect(midX-13.0f, GetWindowHeight()-39.0f, midX+13.0f, GetWindowHeight()-2.0f);
	bar->color = Color(0,16,16,16);
	bar->customType = objFixed;
	fixed.push_back(bar);
	scene.RegisterStatic(*bar);

	// create, update list and register rocks
	Rock * aster = new Rock(80, 80, 1.0f);
	aster->MoveTo(midX-300, midY+80);
	aster->color = Color(128,128,128);
	rocks.push_back(aster);
	scene.RegisterMoving(*aster);

	aster = new Rock(80, 80, -2.0f);
	aster->MoveTo(midX-250, midY-120);
	aster->color = Color(128,128,128);
	rocks.push_back(aster);
	scene.RegisterMoving(*aster);

	aster = new Rock(80, 80, -1.5f);
	aster->MoveTo(midX, midY-270);
	aster->color = Color(128,128,128);
	rocks.push_back(aster);
	scene.RegisterMoving(*aster);

	aster = new Rock(80, 80, 2.0f);
	aster->MoveTo(midX+250, midY-120);
	aster->color = Color(128,128,128);
	rocks.push_back(aster);
	scene.RegisterMoving(*aster);

	aster = new Rock(80, 80, -3.0f);
	aster->MoveTo(midX+300, midY+80);
	aster->color = Color(128,128,128);
	rocks.push_back(aster);
	scene.RegisterMoving(*aster);

	// initialize timer
	secs = 15;
	milisecs = 0;
	timeLimit = secs * freq.QuadPart;

	// initial finish color is green
	finishColor = Color(0, 255, 0);
}

/**********************************************************************************/

void Asteroids::Level2Logic()
{
	if (gameState == PLEVEL2)
	{
		// if player press any action key 
		// begin the level and start timer
		if (input.KeyPressed(DIK_UP)    ||
			input.KeyPressed(DIK_DOWN)  ||
			input.KeyPressed(DIK_LEFT)  || 
			input.KeyPressed(DIK_RIGHT))
		{
			prevState = gameState;
			gameState = LEVEL2;
			QueryPerformanceCounter(&start);
		}

		// do not execute level logic until 
		// state changes from Pre-Level to Level
		return;
	}

	// get elapsed time
	QueryPerformanceCounter(&end);
	LONGLONG remaining = timeLimit - (end.QuadPart - start.QuadPart);
	secs = remaining / freq.QuadPart;
	milisecs = (remaining % freq.QuadPart) * 1000 / freq.QuadPart;

	// if time is up, game over
	if (remaining <= 0)
	{
		// do not show negative number
		secs     = 0;
		milisecs = 0;

		snd.Play(BEEP,0);
		gameState = GAMEOVER;
	}

	// set the finish color
	if (secs < 2)
		// red color
		finishColor = Color(185,0,0);
	else if (secs < 5)
		// yellow color
		finishColor = Color(255,200,0);
	else
		// green color
		finishColor = Color(0,255,0);

	// ---------------------------------------------------

	// Rotate Right
	if (input.KeyPressed(DIK_RIGHT))
		ship.Rotate(4);
	else
	// Rotate Left
	if (input.KeyPressed(DIK_LEFT))
		ship.Rotate(-4);

	// ---------------------------------------------------
	
	// Up Key
	if (input.KeyReleased(DIK_UP))    
		snd.Stop(UP);

	// Down Key
	if (input.KeyReleased(DIK_DOWN))    
		snd.Stop(DOWN);

	// ---------------------------------------------------

	// Translate (move up)
	if (input.KeyPressed(DIK_UP))    
		ship.AccelerateUp();
	if (input.KeyPressRelease(DIK_UP))
		snd.Play(UP,0);
	
	// Translate (move down)
	if (input.KeyPressed(DIK_DOWN))
		ship.AccelerateDown();
	if (input.KeyPressRelease(DIK_DOWN))
		snd.Play(DOWN,0);

	// Space Bar (Fire Missile)
	if (input.KeyPressRelease(DIK_SPACE))
	{
		snd.Play(PULSE,0);
		Missile * mis = new Missile(ship);
		missiles.push_back(mis);
		scene.RegisterMoving(*mis);
	}

	// ---------------------------------------------------

	// move ship by its inertia
	ship.Inertia();

	// move all the missiles
	for (imis = missiles.begin(); imis != missiles.end(); ++imis)
		(*imis)->Move();

	// update all the rocks
	for (iroc = rocks.begin(); iroc != rocks.end(); ++iroc)
		(*iroc)->Update();

	// ---------------------------------------------------

	Rock * ast;

	// test for collision
	if (scene.CollisionDetection())
	{
		// pair of objects in collision
		pair<GameObject*,GameObject*> pairObjs;

		// while the pair returned is not empty, process collision
		while ((pairObjs = scene.OnCollisionObjects()) != pair<GameObject*,GameObject*>(nullptr,nullptr))
		{
			// take first and second object in collision
			Geometry * first  = (Geometry *) pairObjs.first;
			Geometry * second = (Geometry *) pairObjs.second;

			// if rock is the first object, invert pointers
			// so there is no need to consider multiple cases
			if (first->customType == objRock)
			{
				first  = (Geometry *) pairObjs.second;
				second = (Geometry *) pairObjs.first;
			}

			switch (first->customType)
			{
			// -------
			// Missile
			// -------
			case objMissile:

				// Missile and Rock
				if (second->customType == objRock)
				{
					ast = (Rock *) second;

					// if the rock is big, break it in two
					if (ast->GetBBox().GetRadius() >= 80)
					{
						// randomize position of new rocks
						float x1, y1,x2, y2;
						
						switch(rand()%4)
						{
						case 0:
							// quadrante Q1 e Q3
							x1 = ast->x - (ast->GetWidth()/4);
							y1 = ast->y - (ast->GetHeight()/4);
							x2 = ast->x + (ast->GetWidth()/4);
							y2 = ast->y + (ast->GetHeight()/4);
							break;
						case 1:
							// quadrante Q2 e Q4
							x1 = ast->x - (ast->GetWidth()/4);
							y1 = ast->y + (ast->GetHeight()/4);
							x2 = ast->x + (ast->GetWidth()/4);
							y2 = ast->y - (ast->GetHeight()/4);
							break;
						case 2:
							// lado a lado horizontal
							x1 = ast->x - (ast->GetWidth()/4);
							y1 = ast->y;
							x2 = ast->x + (ast->GetWidth()/4);
							y2 = ast->y;
							break;
						case 3:
							// lado a lado vertical
							x1 = ast->x;
							y1 = ast->y - (ast->GetHeight()/4);
							x2 = ast->x;
							y2 = ast->y + (ast->GetHeight()/4);
							break;
						}

						// create two rocks
						Rock * newAster = new Rock(ast->GetWidth()/2, ast->GetHeight()/2, ast->GetSpin());
						newAster->color = Color(128,128,128);
						newAster->MoveTo(x1,y1);
						rocks.push_back(newAster);
						scene.RegisterMoving(*newAster);

						newAster = new Rock(ast->GetWidth()/2, ast->GetHeight()/2, ast->GetSpin());
						newAster->color = Color(128,128,128);
						newAster->MoveTo(x2,y2);
						rocks.push_back(newAster);
						scene.RegisterMoving(*newAster);

						// big rock, start explosion at missile position
						Explosion * exp = new Explosion();
						exp->InitParticles(((Missile *) first)->x, 
							               ((Missile *) first)->y,
										   50, 0.200f, 
										   ast->color);
						explosions.push_back(exp);
					}
					else
					{
						// small rock, start explosion at the center of the rock
						Explosion * exp = new Explosion();
						exp->InitParticles(ast->x, 
							               ast->y,
										   70, 0.250f,
										   ast->color);
						explosions.push_back(exp);
					}

					// Destroy rock
					snd.Play(EXPLOSION,0);
					// unregister rock from scene manager
					scene.UnRegisterMoving(*second);
					// remove rock from the list
					rocks.remove(ast);
					// delete roc object
					delete ast;	

					// Destroy Missile
					// unregister missile from scene manager
					scene.UnRegisterMoving(*first);
					// remove missile from the list
					missiles.remove((Missile*) first);
					// delete missile object
					delete (Missile*) first;
				}
				else
					// Missile and Other Object (except finish marker)
					 if (second->customType != objFinish)
					{
						// missile explosion effect
						Explosion * exp = new Explosion();
						exp->InitParticles(((Missile *) first)->x, 
							               ((Missile *) first)->y,
										   15, 0.1f, 
										   second->color);
						explosions.push_back(exp);

						snd.Play(MISSILEHIT,0);
						// unregister missile from scene manager
						scene.UnRegisterMoving(*first);
						// remove missile from the list
						missiles.remove((Missile*) first);
						// delete missile object
						delete (Missile*) first;
					}

				break;	
			// -------
			//  ship 
			// -------
			case objShip:
				switch (second->customType)
				{
				// Finish Marker
				case objFinish: 
					// player' ship reached finish marker
					if (rocks.empty())
					{
						snd.Play(LEVELUP,0);
						gameState = LEVELCOMPLETE;
						playerTime = (timeLimit / freq.QuadPart) - (secs + 0.001f * milisecs);
					}
					break;

				// Borders or Objects
				case objBorder:
				case objFixed:
				case objRock:
					// player' ship collided
					snd.Play(BLOW,0);
					gameState = GAMEOVER;

					// ship explosion
					Explosion * exp = new Explosion();
					exp->InitParticles(ship.x, 
						               ship.y,
									   30, 0.3f, 
									   ship.color);
					explosions.push_back(exp);
					break;
				}
				break;

			} // end switch
		
		} // end while

	} // end test for collision
}

/**********************************************************************************/

void Asteroids::Level2Draw()
{
	if (gameState == PLEVEL2)
	{
		ballons[0].Draw();
		ballons[1].Draw();
		ballons[2].Draw();

		// Draw Text
		sprintf_s(text, "LEVEL 2");
		sysFont->Draw(0, midY-25, text, Color(0,255,0), GetWindowWidth(), 30, DT_CENTER);

		// Draw Instructions
		sprintf_s(text, "Reach the finish marker in");
		sysFont->Draw(midX+155, midY+152, text, Color(192,128,0));
		sprintf_s(text, "time to complete the level");
		sysFont->Draw(midX+155, midY+172, text, Color(192,128,0));

		sprintf_s(text, "Destroy all the waste");
		sysFont->Draw(midX-388, midY-220, text, Color(192,128,0));
		
		sprintf_s(text, "Move the ship to");
		sysFont->Draw(0, midY+5, text, Color(0,255,0), GetWindowWidth(), 30, DT_CENTER);
		sprintf_s(text, "START");
		sysFont->Draw(0, midY+25, text, Color(0,255,0), GetWindowWidth(), 30, DT_CENTER);

		// Draw Text
		sprintf_s(text, "FINISH");
		sysFont->Draw(finPosX-100, finPosY-35, text, Color(0,255,0), 200, 30, DT_CENTER); 
	}

	// move stars with random velocity
	eSpace->MoveStars();

	// draw stars in new position
	eSpace->DrawStars();

	// draw screen borders
	border[0].Draw();
	border[1].Draw();
	border[2].Draw();
	border[3].Draw();

	// draw rocks
	for (iroc = rocks.begin(); iroc != rocks.end(); ++iroc)
		(*iroc)->Draw();

	// draw missiles
	for (imis = missiles.begin(); imis != missiles.end(); ++imis)
		(*imis)->Draw();

	// draw fixed objects
	for (ifix = fixed.begin(); ifix != fixed.end(); ++ifix)
	{
		switch((*ifix)->type)
		{
		case ObjRectangle: ((Rect *) (*ifix))->Draw(); break;
		case ObjCircle:	((Circle *) (*ifix))->Draw(); break;
		case ObjPolygon: ((Poly *) (*ifix))->Draw(); break;
		}
		
	}

	// animate all the explosions
	iexp = explosions.begin();
	while (iexp != explosions.end())
	{
		if ((*iexp)->Active())
		{
			(*iexp)->MoveParticles();
			(*iexp)->DrawParticles();
			++iexp;
		}
		else
		{
			// clean explosion
			delete *iexp;
			iexp = explosions.erase(iexp);
		}
	}

	if (gameState != LEVELCOMPLETE)
	{
		// draw finish marker
		finish.color = finishColor;
		finish.Draw();		

		// draw player' ship
		if ((gameState != GAMEOVER) || (secs == 0 && milisecs == 0))
			ship.Draw();
	}	

	// draw Text
	sprintf_s(text, "Space Race");
	sysFont->Draw(0, 10, text, Color(128,128,128), GetWindowWidth(), 30, DT_CENTER);

	if (gameState != LEVELCOMPLETE)
	{	
		// draw remaining time in seconds and miliseconds
		sprintf_s(text, MAXBUFFER, "00:%02d", secs);
		sprintf_s(text + 5, MAXBUFFER - 5, ".%03d", milisecs);

		sysFont->Draw(finPosX-100, finPosY-8, text, finishColor, 200, 30, DT_CENTER); 
		
		if (gameState != GAMEOVER)
		{
			sprintf_s(text, "<Arrows> - Move Space ship            <Space> - Fire            <ESC> - Exit Game");
			sysFont->Draw(0, GetWindowHeight()-30, text, Color(128,128,128), GetWindowWidth(), 30, DT_CENTER);
		}
	}
}

/**********************************************************************************/

void Asteroids::Level2CleanUp()
{
	// reset time elapsed
	end.QuadPart = 0;
	start.QuadPart = 0;

	// reset key control states
	input.KeyReset();

	// the game may have ended with objects onscreen (like missiles)
	// remove all remaining objects from lists and delete them

	// clean player' ship
	scene.UnRegisterMoving(ship);

	// clean missiles
	for (imis = missiles.begin(); imis != missiles.end(); ++imis)
	{
		scene.UnRegisterMoving(*((Geometry *) *imis));
		delete *imis;			
	}
	missiles.clear();

	// clean rocks
	for (iroc = rocks.begin(); iroc != rocks.end(); ++iroc)
	{
		scene.UnRegisterMoving(*((Geometry *) *iroc));
		delete *iroc;
	}
	rocks.clear();

	// clean fixed objects
	for (ifix = fixed.begin(); ifix != fixed.end(); ++ifix)
	{
		scene.UnRegisterStatic(*((Geometry *) *ifix));
		delete *ifix;
	}
	fixed.clear();

	// clean explosions
	for (iexp = explosions.begin(); iexp != explosions.end(); ++iexp)
		delete *iexp;
	explosions.clear();
	
	// clean finish marker
	scene.UnRegisterStatic(finish);	
}

/**********************************************************************************/
